/*
 * JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jef.database.meta;

import jef.database.Condition;
import jef.database.DbUtils;
import jef.database.Field;
import jef.database.QueryAlias;
import jef.database.query.Join;
import jef.database.query.JoinElement;
import jef.database.query.JpqlExpression;
import jef.database.query.LazyQueryBindField;
import jef.database.query.Query;
import jef.database.query.RefField;

/**
 * 描述一个连接键对
 * @author Administrator
 *
 */
public class JoinKey extends Condition {
	private static final long serialVersionUID = 1L;

	private boolean isValueField; //右侧是Field
	private boolean isSimple;     //简单模式

	private Query<?> leftLock;  //左侧固定查询对象
	private Query<?> rightLock; //右侧固定查询对象
	// 左右翻转
	public JoinKey flip() {
		if (isSimple) {
			JoinKey r = new JoinKey((Field) this.value, this.operator, this.field);
			r.rightLock=this.leftLock;
			r.leftLock=this.rightLock;
			return r;
		} else {
			return this;
		}
	}

	public Query<?> getLeftLock() {
		return leftLock;
	}

	public Query<?> getRightLock() {
		return rightLock;
	}

	/**
	 * 给定Field和外连接信息，构造连接
	 * 
	 * @param left
	 * @param right
	 */
	public JoinKey(Field left, Field right) {
		this(left,Operator.EQUALS,right);
	}

	public JoinKey(Field field, Operator operator, Object value) {
		this.field = field;
		this.value = value;
		this.operator = operator;
		init();
	}

	private void init() {
		//处理左侧锁定
		if(field instanceof RefField){
			if(((RefField) field).isBind()){
				this.leftLock=((RefField) field).getInstanceQuery(null);	
			}
			field=((RefField) field).getField();
		}else if(field instanceof JpqlExpression){
			JpqlExpression jpql=(JpqlExpression)field;
			if(jpql.isBind()){
				this.leftLock=jpql.getInstanceQuery(null);
			}
		}
		
		isValueField = (value instanceof Field);
		if (isValueField) {
			//处理右侧锁定
			if(value instanceof RefField){
				if(((RefField) value).isBind()){
					this.rightLock=((RefField) value).getInstanceQuery(null);	
				}
				value=((RefField) value).getField();
			}else if(value instanceof JpqlExpression){
				if(((JpqlExpression) value).isBind()){
					this.rightLock=((JpqlExpression) value).getInstanceQuery(null);	
				}
			}
			if (!(field instanceof JpqlExpression)) {
				if ((value instanceof Enum || value instanceof TupleField) && Operator.EQUALS == operator) {
					isSimple = true;
				}
			}
		}
	}

	public boolean isValueField() {
		return isValueField;
	}

	public boolean isSimple() {
		return isSimple;
	}

	public Field getLeft() {
		return field;
	}

	/**
	 * 得到Join关系右边的字段
	 * @return
	 */
	public Field getRightAsField() {
		if (isValueField) {
			return (Field) value;
		} else {
			return null;
		}
	}
	
	public ITableMetadata getLeftTableMeta(){
		if(leftLock!=null){
			return leftLock.getMeta();
		}
		return DbUtils.getTableMeta(this.field);
	}
	
	public ITableMetadata getRightTableMeta(){
		if(rightLock!=null){
			return rightLock.getMeta();
		}
		ITableMetadata meta=null;
		if(this.value instanceof LazyQueryBindField){
			meta=((LazyQueryBindField) value).getMeta();
		}else if(value instanceof Field){
			meta=DbUtils.getTableMeta((Field)value);	
		}
		return meta;
	}
	
	
	public void findAndLockLeft(Join left){
		if(leftLock!=null)return;
		ITableMetadata meta=DbUtils.getTableMeta(this.field);
		if(meta==null)return;
		for(QueryAlias qa:((Join) left).allElements()){
			if(qa.getQuery().getMeta()==meta){
				this.leftLock=qa.getQuery();
				return;
			}
		}	
	}

	public int validate(JoinElement left, Query<?> obj) {
		if(validateLeft(left)){
			return validateRight(obj)?1:0;
		}
		if(validateLeft(obj)){
			return (validateRight(left) ||validateRight(obj))?-1:0;
		}
		return 0;
	}

	private boolean validateLeft(JoinElement q) {
		if(leftLock!=null){
			if(q==leftLock){
				return true;
			}
			if(q instanceof Join){
				for(QueryAlias qa:((Join) q).allElements()){
					if(qa.getQuery()==leftLock){
						return true;
					}
				}
			}
		}else if(field==null){//纯表达式
			return true;
		}else{
			//这里从Field获得meta,但有风险，实际用的meta可能是这个meta的子类。
			ITableMetadata meta=DbUtils.getTableMeta(this.field);
			if(meta==null)return true;
			if(q instanceof Query<?>){
				return isMatch(meta,((Query<?>) q).getMeta());
			}if(q instanceof Join){
				for(QueryAlias qa:((Join) q).allElements()){
					if(isMatch(meta,qa.getQuery().getMeta())){
						return true;
					}
				}	
			}
		}
		return false;
	}

	private boolean isMatch(ITableMetadata meta, ITableMetadata meta2) {
		if(meta==meta2)return true;
		return meta.getThisType().isAssignableFrom(meta2.getThisType());
	}

	private boolean validateRight(JoinElement q) {
		if(rightLock!=null){
			if(q==rightLock){
				return true;
			}
			if(q instanceof Join){
				for(QueryAlias qa:((Join) q).allElements()){
					if(qa.getQuery()==rightLock){
						return true;
					}
				}
			}
		}else{
			ITableMetadata meta=null;
			if(this.value instanceof LazyQueryBindField){
				meta=((LazyQueryBindField) value).getMeta();
			}else if(value instanceof Field){
				meta=DbUtils.getTableMeta((Field)value);	
			}
			if(meta==null)return true;
			if(q instanceof Query<?>){
				return isMatch(meta,((Query<?>) q).getMeta());
			}if(q instanceof Join){
				for(QueryAlias qa:((Join) q).allElements()){
					if(isMatch(meta,qa.getQuery().getMeta())){
						return true;
					}
				}	
			}
		}
		return false;
	}
}
